
#include "configfile.h"
#include <Core/logger/logger.h>

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>

namespace Opl { namespace Core {

        class ConfigFilePrivate{
        public:

            ConfigFilePrivate(){}
            ~ConfigFilePrivate(){}

            QString errorString;
            QString filepath;
            QVariantMap data;

            bool hasValues(const QVariantMap& data, const QString& path, QVariantList& value)
            {
                QVariant v;
                bool b = hasValue(data, path, v);
                if (b == true)
                {
                    value = v.toList();
                    return true;
                }
                return false;
            }

            bool hasValues(const QString& path, QVariantList& value)
            {
                return hasValues(data, path, value);
            }

            bool hasValue(const QVariantMap& data, const QString& path, QVariant& value, int index)
            {
                int idx = path.indexOf(QLatin1Char('/'), index);
                if (idx == -1)
                {
                    return true;
                }

                QString key = path.left(idx);
                bool b = data.contains(key);
                if (b == false)
                    return false;

                QVariant v = data.value(key);
                if (v.type() == QVariant::Map && idx != path.size() - 1)
                    return hasValue(v.toMap(), path.right(path.size() - idx - 1), value);

                value = v;
                return v.isValid();
            }

            bool hasValue(const QVariantMap& data, const QString& path, QVariant& value)
            {
                QString p = path;
                QLatin1Char c = QLatin1Char('/');
                if (!p.endsWith(c))
                    p += c;
                if (p.startsWith(c))
                    p = p.right(p.size() - 1);

                return hasValue(data, p, value, 0);

            }

            bool hasValue(const QString& path, QVariant& value)
            {
                return hasValue(data, path, value);
            }

            bool hasValue(const QVariantMap& data, const QString& section, const QString& key, QVariant& value)
            {
                if (data.contains(section)){
                    QVariantMap m = data.value(section).toMap();
                    if (m.contains(key))
                    {
                        value = m.value(key);
                        return true;
                    }
                }
                return false;
            }

            bool hasValue(const QString& section, const QString& key, QVariant& value)
            {
                return hasValue(data, section, key, value);
            }
        };

        ConfigFile::ConfigFile(const QString& filepath/* = QString()*/) :
                O_PRIVATE_CREATE(ConfigFile)
        {
            O_D(ConfigFile);
            d->filepath = filepath;

        }

        ConfigFile::~ConfigFile()
        {
            O_PRIVATE_DELETE;
        }

        void ConfigFile::setFileName(const QString& fn)
        {
            O_D(ConfigFile);
            d->filepath = fn;
        }

        QString ConfigFile::fileName()const
        {
            O_C(ConfigFile);
            return d->filepath;
        }


        bool ConfigFile::load(const QString& filepath/* = QString()*/)
        {
            qDebug() << "Load Config:" << filepath;
            O_D(ConfigFile);
            d->filepath = filepath;
            return load();
        }

        bool ConfigFile::load()
        {
            O_D(ConfigFile);

            QFile configFile(d->filepath);
            if (!configFile.open(QIODevice::ReadOnly))
            {
                d->errorString = QString("Error in read config file:%1 error: %2").arg( d->filepath, configFile.errorString());
                qWarning() << d->errorString;
                return false;
            }

            QByteArray arr = configFile.readAll();
            QJsonParseError err;
            QJsonDocument document = QJsonDocument::fromJson(arr, &err);
            if (document.isNull() || document.isEmpty())
            {
                d->errorString = QString("An error occurred during parsing:%1 in: %2, code:%3").arg(err.errorString()).arg(err.offset).arg(err.error);
                qWarning() << d->errorString;
                return false;
            }

            QJsonObject obj = document.object();
            d->data = obj.toVariantMap();
            return true;

        }

        bool ConfigFile::save(const QString& filepath)
        {
            O_D(ConfigFile);
            d->filepath = filepath;
            return save();
        }

        bool ConfigFile::save()
        {
            O_D(ConfigFile);

            QFile configFile(d->filepath);
            QFileInfo fi(configFile);
            if (!fi.exists())
            {
                QDir fdir;
                if (!fdir.mkpath(fi.dir().absolutePath()))
                {
                    d->errorString = "unable to create path:" + fi.dir().absolutePath();
                    qWarning() << d->errorString;
                    return false;
                }
            }

            if (!configFile.open(QIODevice::WriteOnly))
            {
                d->errorString = QString("Error in write config file:%1").arg(configFile.errorString());
                qWarning() << d->errorString;
                return false;
            }

            QJsonObject obj = QJsonObject::fromVariantMap(d->data);
            QJsonDocument doc(obj);

            QByteArray data = doc.toJson();
            configFile.write(data);
            configFile.close();
            return true;
        }


        void ConfigFile::setValue(const QString& section, const QString& key, const QVariant& val)
        {
            O_D(ConfigFile);
            QVariantMap smap;
            if (d->data.contains(section))
            {
                smap = d->data.value(section).toMap();
            }

            smap.insert(key, val);
            d->data.insert(section, smap);
        }

        QVariantList ConfigFile::values(const QString& path)
        {
            O_D(ConfigFile);
            QVariantList ret;
            if (d->hasValues(path, ret))
            {
                return ret;
            }

            return QVariantList();
        }

        QVariant ConfigFile::value(const QString& section, const QString& key, const QVariant& def)
        {
            O_D(ConfigFile);
            QVariant ret;

            if (d->hasValue(section, key, ret))
            {
                return ret;
            }

            return def;
        }

        QVariant ConfigFile::value(const QString& path, const QVariant& def/* = QVariant()*/)
        {
            O_D(ConfigFile);
            QVariant ret;

            if (d->hasValue(path, ret))
            {
                return ret;
            }

            return def;
        }

    }}
